Skip to main content
ℬ㏒.㎈ℓℯℛ.ⓧⓨℤ

Jenkins UDP ping-pong (CVE‑2020‑2100)

A Jenkins CVE caught my eye despite being just a DoS.

SECURITY-1641 / CVE-2020-2100
Jenkins 2.218 and earlier, LTS 2.204.1 and earlier supports two network discovery services (UDP multicast/broadcast and DNS multicast) by default. The UDP multicast/broadcast service can be used in an amplification reflection attack, as very few bytes sent to the respective endpoint result in much larger responses: A single byte request to this service would respond with more than 100 bytes of Jenkins metadata which could be used in a DDoS attack on a Jenkins master. Within the same network, spoofed UDP packets could also be sent to make two Jenkins masters go into an infinite loop of replies to one another, thus causing a denial of service.

I decided to replicate the "infinite loop of replies".

Two Jenkins instances #

You can use 2 different machines on a network. Instead, I ran 2 docker containers on the same machine. We want to receive broadcast UDP packets, so just opening the port with -p 33848:33848/udp isn't enough. Instead I used host networking --net=host, but to avoid port clash we needed to use non-default ports in the 2nd instance (default UDP 33848, default HTTP UI 8080).

docker run --rm \
  --net=host \
  --name Jenkins1 \
  jenkins/jenkins:2.200
docker run --rm \
  --net=host \
  -e JAVA_OPTS=-Dhudson.udp=3384 \
  -e JENKINS_OPTS="--httpPort=8081" \
  --name Jenkins2 \
  jenkins/jenkins:2.200

When the services are up, we can play.

Send one special packet #

After receiving the broadcast, Jenkins responds to the source IP and port found in the broadcast UDP message header. Sending a UDP packet to port 33848 will get an XML response from Jenkins1 (nc -u 127.0.0.1 33848). Sending to 3384 will get a response from Jenkins2.

What we can do with UDP in this situation, is spoof the source port in the UDP header. If the Jenkins' weren't on the same machine as the attacker (me), we'd need to also spoof the source IP address. We send one broadcast (255.255.255.255) destined for Jenkins2 (port 3384) but spoof the source port to be Jenkins1 (port 33848).

hping3 -s 33848 -p 3384 --udp --data 1 -c 1 255.255.255.255

(Root required)

Jenkins2 sees our crafted UDP packet with 1 byte of data on port 3384 and replies with ~100 bytes XML to the spoofed source port 33848 i.e. Jenkins1.

Jenkins1 sees a UDP packet on port 33848 and replies with some XML to Jenkins2 port 3384.

Jenkins2 sees a UDP packet on port 3384 and replies with some XML to Jenkins1 port 33848.

Jenkins2 replying to Jenkins1.
Jenkins2 replying to Jenkins1.

Jenkins1 replying to Jenkins2. I had applied some settings on Jenkins1, hence more data is transferred.
Jenkins1 replying to Jenkins2. I had applied some settings on Jenkins1, hence more data is transferred.

Ad infinitum / to infinity and beyond. What a waste of energy. In your process manager of choice you'll see 2 java processes each pegged at ~50% CPU.